#include "gauss.h"
#include <numeric>

#ifdef DEBUG
#include <ostream>
#endif

#ifdef DEBUG
#ifdef LATEX
#define debug_output std::cerr << mat.to_latex() << R"(\\\Rightarrow)" << std::endl;
#else
#define debug_output std::cerr << mat << std::endl;
#endif // LATEX
#else
#define debug_output
#endif // DEBUG

namespace elimination
{
    int gauss_rows(matrix_t &mat)
    {
        int fix = 1;
        for (int k = 0;
             k < std::min(mat.get_rows(), mat.get_cols()) - 1;
             ++k)
        {
            if (mat(k, k) == 0)
            {
                for (int i = k + 1; i < mat.get_rows(); ++i)
                {
                    mat.swap_rows(k, i);
                    if (mat(k, k) != 0)
                        break;
                }
                if (mat(k, k) == 0)
                {
                    // return -1;
                    --fix;
                    continue;
                }
            }
            for (int i = k + fix; i < mat.get_rows(); ++i)
            {
                if (mat(i, k) == 0)
                    continue;
                ration_t scale = -mat(i, k) / mat(k, k);
                mat.add_row(i, k, scale);
            }
            fix = 1;
            debug_output
        }
        return 0;
    }

    int jordan_rows(matrix_t &mat)
    {
        for (int k = std::min(mat.get_rows(), mat.get_cols()) - 1;
             k > 0;
             --k)
        {
            if (mat(k, k) == 0)
            {
                for (int i = k - 1; i >= 0; --i)
                {
                    mat.swap_rows(k, i);
                    if (mat(k, k) != 0)
                        break;
                }
                if (mat(k, k) == 0)
                    return -1;
            }
            for (int i = k - 1; i >= 0; --i)
            {
                if (mat(i, k) == 0)
                    continue;
                ration_t scale = -mat(i, k) / mat(k, k);
                mat.add_row(i, k, scale);
            }
            debug_output
        }
        return 0;
    }

    void norm_rows(matrix_t &mat)
    {
        for (int i = 0; i < mat.get_rows(); ++i)
        {
            int j = 0;
            while ((mat(i, j) == 0) && (j < mat.get_cols()))
                ++j;
            ration_t scale = mat(i, j);
            if (scale == 0)
                continue;
            scale = ration_t{1} / scale;
            mat.scale_row(i, scale);
        }
    }
}